package com.bml.architect.spring.beanpost;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import org.springframework.stereotype.Component;

import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * @author: baiml
 * @Title: MyBeanPostProcessor
 * @ProjectName: 中台技术平台
 * @Depart 中台研发部
 * @Description:
 * @date: 2021/9/7 16:19
 */
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    private final String LISTEN_BEAN_NAME = "myListener";

    @Override
    public Object postProcessAfterInitialization( Object bean, String beanName) throws BeansException {

        if(beanName.equals(LISTEN_BEAN_NAME)){
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(bean.getClass());
            enhancer.setUseCache(false);
            final  Object ob = bean;
            enhancer.setCallback(new MethodInterceptor() {
                @Override
                public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                Object result = null;
                    System.out.println("监听器代理...."+ method.getName());
                    if (Object.class.equals(method.getDeclaringClass())) {
                        result = method.invoke(ob, objects);
                    }
                    return method.invoke(ob,objects);
                }
            });
            bean = enhancer.create();
        }

        return bean;
    }
    /**
     * invoke default method
     *
     * @param proxy  proxy object
     * @param method proxy invoke method
     * @param args   method args
     * @return
     * @throws Throwable
     */
    private Object invokeDefaultMethod(Object proxy, Method method, Object[] args) throws Throwable {
        final Constructor<MethodHandles.Lookup> constructor = MethodHandles.Lookup.class.getDeclaredConstructor(Class.class, int.class);
        if (!constructor.isAccessible()) {
            constructor.setAccessible(true);
        }
        final Class<?> declaringClass = method.getDeclaringClass();
        return constructor.newInstance(declaringClass, MethodHandles.Lookup.PRIVATE)
                .unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
    }

    /**
     * check is default method
     *
     * @param method
     * @return
     */
    private boolean isDefaultMethod(Method method) {
        return ((method.getModifiers() & (Modifier.ABSTRACT | Modifier.PUBLIC | Modifier.STATIC)) == Modifier.PUBLIC) && method.getDeclaringClass().isInterface();
    }


}
